package com.cloud.async.business.context;

import com.cloud.async.business.body.BusinessResult;
import com.cloud.async.error.ErrorHandler;
import com.cloud.async.monitor.ResultHandlerMonitor;
import com.cloud.async.result.core.AsyncResult;
import com.cloud.async.business.core.BaseAsyncResultHandler;
import com.cloud.async.result.core.handler.DefaultAsyncResultHandlerWrapper;
import com.cloud.async.retry.RetryPolicy;
import com.cloud.async.util.ClassAsyncResultUtils;
import com.cloud.async.util.ex.AsyncException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.StringUtils;

import java.util.concurrent.Executor;
import java.util.concurrent.atomic.LongAdder;
import java.util.function.Supplier;

/**
 * @author chengrui
 * @title: AbstractAsyncContext
 * @projectName webCloudAsync
 * @description: 抽象及其相应实现，业务实现核心类
 * @date 2021/9/26 21:49
 */
public abstract class AbstractAsyncContext extends AsyncTaskContext {

    /**
     * logger
     */
    private final Log logger = LogFactory.getLog(getClass());

    /**
     * 设置线程池
     */
    private Executor executor;

    /**
     * 全局参数进行设置获取结果的等待时间
     */
    private Long globalTimeout = 1000L;

    private volatile AsyncResult result;

    private void initializeAsyncResult(Class<?> resultHandler, Class<? extends ErrorHandler> errorHandler,
                                       Class<? extends ResultHandlerMonitor> resultHandlerMonitor) {
        if (this.result == null) {
            synchronized (this) {
                if (this.result == null) {
                    BaseAsyncResultHandler handler = ClassAsyncResultUtils.createAsyncResultHandler(globalTimeout,
                            this, resultHandler, errorHandler, resultHandlerMonitor);
                    this.result = new DefaultAsyncResultHandlerWrapper(handler);
                }
            }
        }
    }

    private void initializeAsyncResult(Class<?> resultHandler, ErrorHandler errorHandler,
                                       ResultHandlerMonitor resultHandlerMonitor) {
        if (this.result == null) {
            synchronized (this) {
                if (this.result == null) {
                    BaseAsyncResultHandler handler = ClassAsyncResultUtils.createAsyncResultHandlerExp(globalTimeout,
                            this, resultHandler, errorHandler, resultHandlerMonitor);
                    this.result = new DefaultAsyncResultHandlerWrapper(handler);
                }
            }
        }
    }


    protected void initialize(Executor executor, Long globalTimeout,
                              Class<?> resultHandler,
                              Class<? extends ErrorHandler> errorHandlerCls,
                              Class<? extends ResultHandlerMonitor> resultHandlerMonitorCls,
                              Class<? extends RetryPolicy> retryPolicyCls,
                              ErrorHandler errorHandler,
                              ResultHandlerMonitor resultHandlerMonitor,
                              RetryPolicy retryPolicy) {
        this.executor = executor;
        this.globalTimeout = globalTimeout;
        if (errorHandlerCls != null || resultHandlerMonitorCls != null) {
            initializeAsyncResult(resultHandler, errorHandlerCls, resultHandlerMonitorCls);
        } else {
            initializeAsyncResult(resultHandler, errorHandler, resultHandlerMonitor);
        }
        // 初始化重试策略&等待时间
        initRetryPolicy(ClassAsyncResultUtils.createRetryPolicy(retryPolicyCls, retryPolicy));
    }

    public AbstractAsyncContext() {
    }


    private final LongAdder taskCounter = new LongAdder();

    protected String getId() {
        taskCounter.increment();
        return String.format("Tsk%012d", taskCounter.longValue());
    }

    @Override
    public <T> BusinessResult doBusiness(Supplier<T> task, Class<T> rsp) {
        String taskId = getId();
        return doBusiness(task, taskId, rsp);
    }

    @Override
    public <T> BusinessResult doBusiness(Supplier<T> task, String taskId, Class<T> rsp) {
        if (StringUtils.isEmpty(taskId)) {
            throw new AsyncException("taskId must not be empty");
        }

        if (task == null || rsp == null) {
            throw new AsyncException("Task and response type must not be null");
        }

        // 成功则直接返回
        return businessLogic(task, taskId, rsp, executor);
    }


    @Override
    public AsyncResult result() {
        if (this.result == null) {
            synchronized (this) {
                if (this.result == null) {
                    BaseAsyncResultHandler handler = ClassAsyncResultUtils.createAsyncResultHandler(globalTimeout,
                            this, null, null, null);
                    this.result = new DefaultAsyncResultHandlerWrapper(handler);
                }
            }
        }
        return result;
    }
}
